package flinty.autocomplete.combobox;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * simplest successor of BaseAutoCompleteComboBoxModel which is usefull when we
 * know a full list of items to be displayed (idependent on text pattern typed
 * at combobox for autocompletion ) and this list is not very long
 * 
 * @param <T>
 *            is a class of items displayed in combobox
 */
public class SimpleListComboBoxModel<T> extends
		BaseAutoCompleteComboBoxModel<T> {

	private static final long serialVersionUID = 610768006858958816L;

	private final DataProvider<T> provider;

	private final StringConverter<T> converter;

	private final Map<ItemWrapper<T>, String> renderData = new HashMap<ItemWrapper<T>, String>();

	private final Map<ItemWrapper<T>, String> lowerCase = new HashMap<ItemWrapper<T>, String>();

	private final Map<String, ItemWrapper<T>> inverse = new HashMap<String, ItemWrapper<T>>();

	private final List<ItemWrapper<T>> allData = new LinkedList<ItemWrapper<T>>();

	private final List<ItemWrapper<T>> filtered = new LinkedList<ItemWrapper<T>>();

	private final List<ItemWrapper<T>> preprocessed = new LinkedList<ItemWrapper<T>>();

	/**
	 * 
	 * @param prov
	 *            provider of data that is to be displayed in combobox
	 * @param rend
	 *            converter that represent item of class T as a String
	 */
	public SimpleListComboBoxModel(DataProvider<T> prov, StringConverter<T> rend) {
		provider = prov;
		converter = rend;
	}

	@Override
	protected List<ItemWrapper<T>> getFilteredData() {
		return filtered;
	}

	@Override
	protected void loadData() {
		allData.clear();
		filtered.clear();
		preprocessed.clear();
		for (T item : provider.getData(null)) {
			allData.add(new ItemWrapper<T>(item, item == null ? null
					: converter.render(item)));
		}
		filtered.addAll(allData);
		renderData.clear();
		lowerCase.clear();
		inverse.clear();
		for (ItemWrapper<T> item : allData) {
			if (item == null) {
				continue;
			}
			String name = item.toString();
			renderData.put(item, name);
			name = name.toLowerCase();
			lowerCase.put(item, name);
			inverse.put(name, item);
		}

	}

	@Override
	protected boolean preprocessFiltering(String pattern) {
		if (data == null) {
			loadData();
			data = allData;
		}
		if (pattern == null || pattern.trim().length() == 0) {
			preprocessed.clear();
			preprocessed.addAll(allData);
			return true;
		}
		preprocessed.clear();
		for (ItemWrapper<T> item : allData) {
			if (lowerCase.get(item).contains(pattern)) {
				preprocessed.add(item);
			}
		}
		return preprocessed.size() > 0;
	}

	@Override
	protected void updateFilteredData() {
		filtered.clear();
		filtered.addAll(preprocessed);
	}

	@Override
	protected void resetFilteredData() {
		filtered.clear();
		filtered.addAll(allData);
	}

	@Override
	protected boolean isValid(ItemWrapper<T> anObject) {
		return anObject == null || allData.contains(anObject);
	}
}
